#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from socketserver import BaseRequestHandler, TCPServer
from pbdata import RawPBPack, PBMsgHeader, PBMsgStats
from pbdata import STAT_DATA_START, STAT_DATA_CHUNK, STAT_DATA_END
from settings import Settings


class DataHandler(BaseRequestHandler):
    buffer = bytes()        # 接收数据缓冲区
    offset = 0              # 分包偏移量
    saver_list = []         # 存储器列表
    header = []             # 数据表头

    def init_saver(self, identifier):
        """
        初始化数据存储方式
        :param identifier: 客户端标识符，(user, datetime)二元组
        """
        self.saver_list = [saver(identifier) for saver in self.saver_list]

    def callback_saver_method(self, callback, *args, **kwargs):
        """
        回调存储方式的存储方法
        :param callback: 回调函数
        :return:
        """
        for sv in self.saver_list:
            _callback = getattr(sv, callback)
            _callback(*args, **kwargs)

    def del_saver(self):
        """
        销毁所有的saver对象
        :return:
        """
        for sv in self.saver_list:
            del sv

    def _handle_msg(self, pack):
        """
        处理消息，将完整的消息包解析成header或stat数据结构然后清空缓冲区并归0偏移量
        :param pack: 完整的二进制包
        :return:
        """
        # 获取消息类型
        msg = RawPBPack(pack)
        msg_typ = msg.typ
        del msg
        # 如果是开始消息则获取客户端标识符，然后获取header，初始化saver后写入header
        if msg_typ == STAT_DATA_START:
            msg = PBMsgHeader(pack)
            user, dt = msg.user_name, msg.datetime
            self.header = msg.get_header()
            self.init_saver((user, dt))
            self.callback_saver_method("save_header", self.header)
            del msg
        # 如果是数据消息则获取stats
        if msg_typ == STAT_DATA_CHUNK:
            msg = PBMsgStats(pack)
            stats = msg.get_stats()
            self.callback_saver_method("save_stats", stats)
            del msg
        # 如果是结束消息则关闭socket
        if msg_typ == STAT_DATA_END:
            self.finish()
        self.buffer = bytes()
        self.offset = 0

    def handle(self):
        print("Got connection from {0}:{1}".format(self.client_address[0], self.client_address[1]))

        while True:
            # 获取socket的文件描述符，如果大于0则收包，否则关闭socket
            fd = self.request.fileno()
            if fd > 0:
                pack = self.request.recv(1024)
                pack_len = len(pack)

                # 如果包长为0则跳出循环
                if pack_len == 0:
                    break

                # 缓冲区没有数据说明是新包
                if len(self.buffer) == 0:
                    msg = RawPBPack(pack)
                    # 消息长度大于包长度则将包放入缓冲区并修改偏移量
                    if msg.length > pack_len:
                        self.buffer += pack
                        self.offset = msg.length - pack_len
                    # 处理完整消息
                    else:
                        self._handle_msg(pack)

                # 缓冲区有数据则需要粘包
                else:
                    # 偏移量大于包长度则继续粘包并修改偏移量
                    if self.offset > pack_len:
                        self.buffer += pack
                        self.offset -= pack_len
                    # 粘包后缓冲区中是一个完整的消息包，对其进行处理
                    else:
                        self.buffer += pack
                        self._handle_msg(self.buffer)

    def setup(self):
        self.request.settimeout(Settings.CLIENT_SETUP_TIMEOUT)

    def finish(self):
        self.del_saver()
        self.request.close()
        print("Disconnect from {}:{}".format(self.client_address[0], self.client_address[1]))


if __name__ == '__main__':
    from threading import Thread
    from saver import get_saver_list

    nworkers = Settings.CLIENT_MAX_NUM
    port = Settings.SERVER_LISTENING_PORT

    DataHandler.saver_list = get_saver_list()
    TCPServer.allow_reuse_address = True
    serv = TCPServer(('', port), DataHandler)
    print("Server started on 127.0.0.1, port {}.....".format(port))
    for n in range(nworkers):
        t = Thread(target=serv.serve_forever)
        t.daemon = True
        t.start()
    serv.serve_forever()
